package com.wtwd.device.service.impl;


import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.wtwd.device.constant.SeparatorConstant;
import com.wtwd.device.dao.ScheduleTaskMapper;
import com.wtwd.device.model.entity.ScheduleTask;
import com.wtwd.device.service.*;
import com.wtwd.device.util.DateUtils;
import com.wtwd.device.util.SpringContextUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zdl
 * @since 2021-09-10
 */
@Service
public class ScheduleTaskServiceImpl implements ScheduleTaskService, SchedulingConfigurer {

    private static Logger log = LoggerFactory.getLogger(ScheduleTaskServiceImpl.class);

    @Resource
    private ScheduleTaskMapper scheduleTaskMapper;

    @Resource
    private RedisServiceImpl redisService;

    @Resource
    private ScheduleTaskService scheduleTaskService;

    @Resource
    private AppDeviceService appDeviceService;

    @Resource
    private HeartRateService heartRateService;

    @Resource
    private StepService stepService;

    @Resource
    private SleepRecordService sleepRecordService;

    @Resource
    private BloodPressureRecordService bloodPressureRecordService;

    @Resource
    private BloodOxygenRecordService bloodOxygenRecordService;

    @Resource
    private BloodSugarRecordService bloodSugarRecordService;

    @Resource
    private TempRecordService tempRecordService;

    @Autowired
    private AppSportRecordService appSportRecordService;


    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        List<ScheduleTask> tasks = getAllScheduleTasks();
        //通过校验的数据执行任务
        int count = 0;
        if (tasks != null && tasks.size() > 0) {
            for (int i = 0; i < tasks.size(); i++) {
                try {
                    scheduledTaskRegistrar.addTriggerTask(getRunnable(tasks.get(i)), getTrigger(tasks.get(i)));
                    count++;
                } catch (Exception e) {
                    log.error("task start error:" + tasks.get(i).getExecutor() + ";" + e.getMessage());
                }
            }
        }
        log.info("started task number=" + count);

    }

    private Trigger getTrigger(ScheduleTask scheduleTask) {
        return new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                //将Cron 0/1 * * * * ?
                CronTrigger trigger = new CronTrigger(scheduleTask.getCron());
                Date nextExec = trigger.nextExecutionTime(triggerContext);
                return nextExec;
            }

        };
    }

    String getClassName(String executor) {
        return executor.substring(0, executor.lastIndexOf(SeparatorConstant.DOT));
    }

    /**
     * 集群任务
     * 1.获取任务状态(未执行,正在执行,已执行)
     * -未被执行,更新任务状态为正在执行,继续下一步
     * -正在执行(其他服务器正在执行),持续等待执行结果
     * 2.执行任务
     * -执行失败,更新任务状态为未执行(其他服务获取任务执行)
     * -执行成功,更新任务状态为已完成,其他服务器获取任务该状态后,退出任务调度
     * <p>
     * 说明:此方案需明确规定执行时间
     * <p>
     * 问题:
     * 1.规定锁时间内任务未执行完
     * -继续执行(锁已释放),采用
     * -放弃让其他服务器执行
     *
     */
    private Runnable getRunnable(ScheduleTask scheduleTask) {
        return new Runnable() {
            @Override
            public void run() {
                String executor = scheduleTask.getExecutor();
                boolean locked = false;
                Boolean isRollbackVersion = null;
                ScheduleTask currentTask = null;
                try {
                    //预备执行的版本
                    String preVersion = DateUtils.format(new Date(), DateUtils.dateFormat17);
                    log.warn("Timed Task start execute  time:{}---------------------", preVersion);
                    while (true) {
                        locked = redisService.lock(executor, InetAddress.getLoopbackAddress().getHostAddress(), scheduleTask.getLockedTime());
                        if (!locked) {
                            log.info("Timed Task other server execute---------------------");
                            try {
                                Thread.sleep(scheduleTask.getLockedTime() / 2);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        } else {
                            //获取任务执行状态
                            currentTask = getScheduleTaskById(scheduleTask.getId());
                            String currentVersion = currentTask.getVersion();
                            String executeStatus = currentTask.getExecuteStatus();
                            //预备执行版本号大于当前版本号
                            boolean isChangeVersion = currentVersion == null ? true : preVersion.compareTo(currentVersion) > 0;
                            if (isChangeVersion && !"1".equals(executeStatus)) {
                                //修改任务状态,正在执行
                                ScheduleTask upScheduleTask = new ScheduleTask();
                                upScheduleTask.setId(currentTask.getId());
                                upScheduleTask.setExecuteStatus("1");
                                upScheduleTask.setVersion(preVersion);
                                updateById(upScheduleTask);
                                isRollbackVersion = true;
                                //执行任务,
                                Object obj = SpringContextUtil.getBean(getClassName(executor));
                                Method method = obj.getClass().getMethod(getMethodName(executor), null);
                                method.invoke(obj);
                                //更新任务状态,已完成
                                upScheduleTask.setExecuteStatus("2");
                                updateById(upScheduleTask);
                                isRollbackVersion = false;
                                log.info("Task succuss execute: {}---------------------", executor);
                                break;
                            }
                            if (!isChangeVersion && "2".equals(executeStatus)) {
                                break;
                            }

                        }
                    }
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } finally {
                    if (locked) {//拿到锁的服务器, 完成任务或出现异常释放锁
                        //任务执行失败,版本回退
                        if (isRollbackVersion != null && isRollbackVersion) {
                            ScheduleTask upScheduleTask = new ScheduleTask();
                            upScheduleTask.setId(scheduleTask.getId());
                            upScheduleTask.setVersion(currentTask.getVersion());
                            upScheduleTask.setExecuteStatus("2");
                            scheduleTaskService.updateById(upScheduleTask);
                        }
                        redisService.unlock(executor, InetAddress.getLoopbackAddress().getHostAddress());
                    }
                }

            }
        };
    }

    String getMethodName(String executor) {
        return executor.substring(executor.lastIndexOf(SeparatorConstant.DOT) + 1);
    }

    private List<ScheduleTask> getAllScheduleTasks() {
        return new LambdaQueryChainWrapper<>(scheduleTaskMapper)
                .eq(ScheduleTask::getIsEnable, 1)
                .list();
    }

    private ScheduleTask getScheduleTaskById(Integer id) {
        return new LambdaQueryChainWrapper<>(scheduleTaskMapper)
                .eq(ScheduleTask::getId, id)
                .one();
    }

    @Override
    public boolean updateById(ScheduleTask scheduleTask) {
        return scheduleTaskMapper.updateById(scheduleTask) == 1;
    }

    public void clearSportData() {
//      appDeviceService.clearSportDataByDate(DateUtils.format(DateUtils.getDateByMonth(-3, new Date()), DateUtils.dateFormat5));
        //清除步数数据(3月前数据删除)
        stepService.clearStepByDate(DateUtils.format(DateUtils.getDateByMonth(-3, new Date()), DateUtils.dateFormat5));
        //清除心率数据(3月前数据删除)
        heartRateService.clearHeartRateByDate(DateUtils.format(DateUtils.getDateByMonth(-3, new Date()), DateUtils.dateFormat5));
        //清除睡眠数据
        sleepRecordService.clearSleepRecordByDate(DateUtils.format(DateUtils.getDateByMonth(-3, new Date()), DateUtils.dateFormat5));
        //清除血压数据
        bloodPressureRecordService.clearBloodPressureRecordByDate(DateUtils.format(DateUtils.getDateByMonth(-3, new Date()), DateUtils.dateFormat5));
        //清除血氧数据
        bloodOxygenRecordService.clearBloodOxygenRecordByDate(DateUtils.format(DateUtils.getDateByMonth(-3, new Date()), DateUtils.dateFormat5));
        //清除血糖数据
        bloodSugarRecordService.clearBloodSugarRecordByDate(DateUtils.format(DateUtils.getDateByMonth(-3, new Date()), DateUtils.dateFormat5));
        //清除体温数据
        tempRecordService.clearTempRecordByDate(DateUtils.format(DateUtils.getDateByMonth(-3, new Date()), DateUtils.dateFormat5));
        //清除运动记录(并清除掉定位文件数据)
        appSportRecordService.clearSportRecordByDate(DateUtils.format(DateUtils.getDateByMonth(-3, new Date()), DateUtils.dateFormat5));
    }
}


